<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Set 和 Map 数据结构</title>
	</head>
	<body>
		<p>ES6 提供了新的数据结构 Set。它类似于数组，但是成员的值都是唯一的，没有重复的值</p>
		<p>JavaScript 的对象（Object），本质上是键值对的集合（Hash 结构），但是传统上只能用字符串当作键。这给它的使用带来了很大的限制，为了解决这个问题，ES6 提供了 Map 数据结构。它类似于对象，也是键值对的集合，但是“键”的范围不限于字符串，各种类型的值（包括对象）都可以当作键。</p>

		<script type="text/javascript">
			// 空参构造
			const s = new Set();
			// 赋值
			[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
			// 通过数组构造
			const s1 = new Set([1, 2, 3, 4, 4]);
			// 通过类似数组的对象构造
			const s2 = new Set(document.querySelectorAll('p'));

			//特殊用法
			let arr = [2, 5, 4, 4, 2];
			// 数组去重
			console.log(...[...new Set(arr)]);
			// 字符串去重
			console.log([...new Set('ababbc')].join(''));

			// 向 Set 加入值的时候，不会发生类型转换，所以5和"5"是两个不同的值。
			// Set 内部判断两个值是否不同，使用的算法叫做“Same-value-zero equality”，它类似于精确相等运算符（===）
			// 主要的区别是向 Set 加入值时认为NaN等于自身，而精确相等运算符认为NaN不等于自身
			// 另外，由于两个空对象不相等，所以它们被视为两个值

			// WeakSet 结构与 Set 类似，也是不重复的值的集合。但是，它与 Set 有两个区别
			// 首先，WeakSet 的成员只能是对象，而不能是其他类型的值，如果存入就会报错
			// WeakSet 没有size属性，没有办法遍历它的成员。WeakSet 不能遍历，是因为成员都是弱引用，随时可能消失，遍历机制无法保证成员的存在，很可能刚刚遍历结束，成员就取不到了
			const a = [
				[1, 2],
				[3, 4]
			];
			const ws = new WeakSet(a); // WeakSet {[1, 2], [3, 4]}

			// 事实上，不仅仅是数组，任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数
			const map = new Map([
				['name', '张三'],
				['title', 'Author']
			]);
			
			// WeakMap结构与Map结构类似，也是用于生成键值对的集合，WeakMap与Map的区别有两点
			// 首先，WeakMap只接受对象作为键名（null除外），不接受其他类型的值作为键名
			// 其次，WeakMap的键名所指向的对象，不计入垃圾回收机制
			
			// WeakMap的设计目的在于，有时我们想在某个对象上面存放一些数据，但是这会形成对于这个对象的引用
			const e1 = document.getElementById('foo');
			const e2 = document.getElementById('bar');
			const arr = [
			  [e1, 'foo 元素'],
			  [e2, 'bar 元素'],
			];
			// 一旦不再需要这两个对象，我们就必须手动删除这个引用，否则垃圾回收机制就不会释放e1和e2占用的内存
			arr [0] = null;
			arr [1] = null;
			// 上面这样的写法显然很不方便。一旦忘了写，就会造成内存泄露
			// WeakMap 就是为了解决这个问题而诞生的，它的键名所引用的对象都是弱引用，即垃圾回收机制不将该引用考虑在内。因此，只要所引用的对象的其他引用都被清除，垃圾回收机制就会释放该对象所占用的内存。也就是说，一旦不再需要，WeakMap 里面的键名对象和所对应的键值对会自动消失，不用手动删除引用
			const wm = new WeakMap();
			const element = document.getElementById('example');
			wm.set(element, 'some information');
			wm.get(element) // "some information"
			
			// WeakMap 与 Map 在 API 上的区别主要是两个:
			// 一是没有遍历操作（即没有keys()、values()和entries()方法），也没有size属性。因为没有办法列出所有键名，某个键名是否存在完全不可预测，跟垃圾回收机制是否运行相关。这一刻可以取到键名，下一刻垃圾回收机制突然运行了，这个键名就没了，为了防止出现不确定性，就统一规定不能取到键名。
			// 二是无法清空，即不支持clear方法。因此，WeakMap只有四个方法可用：get()、set()、has()、delete()。
		</script>
	</body>
</html>
